package com.jy.rhin.infra.repo.notfication.publish.query;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jy.framework.base.entity.Page;
import com.jy.framework.base.utils.AssertUtils;
import com.jy.framework.base.utils.Converter;
import com.jy.framework.base.utils.DateUtil;
import com.jy.rhin.domain.exception.RhinInfraErrorType;
import com.jy.rhin.domain.notification.publish.entity.EnumPublishMessageNotifyStatus;
import com.jy.rhin.infra.exception.RhinInfraException;
import com.jy.rhin.infra.repo.notfication.publish.mapper.PublishMessageMapper;
import com.jy.rhin.infra.repo.notfication.publish.mapper.PublishMessageStatisticsMapper;
import com.jy.rhin.infra.repo.notfication.publish.po.PublishMessagePO;
import com.jy.rhin.infra.service.CacheService;
import com.jy.rhin.sdk.dto.notification.SubscriberShortDto;
import com.jy.rhin.sdk.dto.notification.req.CountDateNotificationDto;
import com.jy.rhin.sdk.dto.notification.req.FailPublishMessageReqDto;
import com.jy.rhin.sdk.dto.notification.req.PublishMessagePageReqDto;
import com.jy.rhin.sdk.dto.notification.resp.*;
import com.jy.starter.repository.utils.PageUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.text.ParseException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;


/**
 * @author dhl
 * @date 2022/6/30
 * @Description
 */
@Service
public class PublishMessageQueryHandler {

    @Resource
    private PublishMessageMapper publishMessageMapper;

    @Resource
    PublishMessageStatisticsMapper publishMessageStatisticsMapper;

    @Resource
    CacheService cacheService;

    private static final String DATE_FORMAT = "yyyy-MM-dd";

    /**
     * 推送通知分页查询
     *
     * @param req
     * @return
     */
    public Page<PublishMessagePageDto> queryPublishMessagePage(PublishMessagePageReqDto req) {
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<PublishMessagePageDto> page = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(req.getCurrent(), req.getSize(), true);
        QueryWrapper<PublishMessagePageDto> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(!StringUtils.isEmpty(req.getPublisherCode()), "a.publisher_code", req.getPublisherCode()).eq(!StringUtils.isEmpty(req.getSubscriberCode()), "a.subscriber_code", req.getSubscriberCode()).eq(!Boolean.TRUE.equals(req.getHasFail()), "a.notify_status", "DONE").and(!StringUtils.isEmpty(req.getText()), wp -> wp.like("c.subscribe_uid", req.getText()).or().like("d.topic_name", req.getText())).orderByDesc("a.create_time");
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<PublishMessagePageDto> queryPublishMessagePage = publishMessageMapper.queryPublishMessagePage(page, queryWrapper);

        return PageUtils.page(queryPublishMessagePage);
    }

    /**
     * 通知详情数据获取
     *
     * @param id
     * @return
     */
    public PublishMessageDetailDto queryPublishMessageDetail(Long id) {
        QueryWrapper<PublishMessageDetailDto> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("a.id", id);
        return publishMessageMapper.queryPublishMessageDetail(queryWrapper);
    }

    /**
     * 查询topic的所有订阅记录
     *
     * @param topicId
     * @return
     */
    public List<SubscribeAndFilterDto> querySubscribeAndFilterListByTopicId(Long topicId) {
        return publishMessageMapper.querySubscribeAndFilterListByTopicId(topicId);
    }

    /**
     * 获取发送失败的消息列表
     *
     * @param req
     * @return
     */
    public List<PublishMessageDto> querySendFailPublishMessageList(FailPublishMessageReqDto req) {
        Date endTime = new Date();
        Date startTime = DateUtils.addDays(endTime, -req.getQueryDay());
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<PublishMessageDto> page = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(-1, -1);
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<PublishMessageDto> pageRes = publishMessageMapper.querySendFailPublishMessageList(page, req.getPublishCount(), startTime, endTime);
        return pageRes.getRecords();
    }

    public Page<PublishMessageDto> querySendFailPublishMessagePage(FailPublishMessageReqDto req) {
        Date endTime = new Date();
        Date startTime = DateUtils.addDays(endTime, -req.getQueryDay());
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<PublishMessageDto> page = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(req.getCurrent(), req.getSize());
        try {
            com.baomidou.mybatisplus.extension.plugins.pagination.Page<PublishMessageDto> pageRes = publishMessageMapper.querySendFailPublishMessageList(page, req.getPublishCount(), startTime, endTime);
            return PageUtils.page(pageRes);
        } catch (Exception e) {
            e.printStackTrace();
            return new Page<>();
        }
    }

    /**
     * 统计指定日期通知次数
     *
     * @param dto
     * @return
     */
    public List<CountDateNotificationRespDto> countDateNotification(CountDateNotificationDto dto) {
        AssertUtils.notTrue(StringUtils.isEmpty(dto.getStartDate()) || StringUtils.isEmpty(dto.getEndDate()), RhinInfraErrorType.START_END_DATE_NOT_NULL);
        Date startDate = Converter.toDate(dto.getStartDate(), DATE_FORMAT);
        Date endDate = Converter.toDate(dto.getEndDate(), DATE_FORMAT);
        List<CountDateNotificationRespDto> countList = publishMessageStatisticsMapper.queryStatistics(startDate, endDate);
        //统计一段时间内每天的数据,无数据补充0
        return getCountDateNotificationRespDtos(dto, countList);
    }

    /**
     * 统计一段时间内每天的数据,无数据补充0
     *
     * @param dto
     * @param countList
     * @return
     */
    private List<CountDateNotificationRespDto> getCountDateNotificationRespDtos(CountDateNotificationDto dto, List<CountDateNotificationRespDto> countList) {
        if (Objects.nonNull(dto.getStartDate()) && Objects.nonNull(dto.getEndDate())) {
            try {
                String start = dto.getStartDate();
                Date startDate = Converter.toDate(start);
                String end = dto.getEndDate();
                Date endDate = Converter.toDate(end);
                //获取开始日期和结束日期之间的间隔天数
                int days = DateUtil.dateBetween(startDate, endDate) + 1;
                if (countList.size() < days) {
                    Map<String, String> dateMap = CollectionUtils.isEmpty(countList) ? new HashMap<>() : listToMap(countList);
                    //获取连续日期的时间区间列表
                    List<String> stringList = completionDate(Converter.dateToLocalDateTime(startDate), Converter.dateToLocalDateTime(endDate));
                    List<CountDateNotificationRespDto> returnList = new ArrayList<>();
                    String nowDate = Converter.toDateStr(new Date());
                    for (int i = stringList.size() - 1; i >= 0; i--) {
                        returnList.add(getCountDto(dateMap, stringList, i, nowDate));
                    }
                    return returnList;
                }
            } catch (ParseException e) {
                throw new RhinInfraException(RhinInfraErrorType.DATE_CONVERTER_ERROR);
            }
        }
        return countList;
    }

    public List<SubscriberShortDto> queryMessageSubscriberListByMsgId(String msgId) {
        QueryWrapper<SubscriberShortDto> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("a.message_id", msgId).orderByAsc("a.create_time");
        return publishMessageMapper.queryMessageSubscriberListByMsgId(queryWrapper);
    }

    /**
     * 封装对应日期记录数
     *
     * @param dateMap    查询的日期统计数Map
     * @param stringList 日期list
     * @param i          下标
     * @param nowDate    当前日期
     * @return
     */
    private CountDateNotificationRespDto getCountDto(Map<String, String> dateMap, List<String> stringList, int i, String nowDate) {
        String strDate = stringList.get(i);
        String total = Objects.equals(nowDate, strDate) ? getNowDateTotal(nowDate).toString() : dateMap.getOrDefault(strDate, "0");
        CountDateNotificationRespDto countDto = new CountDateNotificationRespDto();
        countDto.setTotal(total);
        countDto.setNotifyDate(Converter.toDate(strDate));
        return countDto;
    }


    /**
     * 获取连续日期的时间区间列表
     *
     * @param startDate
     * @param endDate
     * @return
     */
    private static List<String> completionDate(LocalDateTime startDate, LocalDateTime endDate) {
        //日期格式化
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
        List<String> dateList = new ArrayList<>();
        //遍历给定的日期期间的每一天
        for (int i = 0; !Duration.between(startDate.plusDays(i), endDate).isNegative(); i++) {
            //添加日期
            dateList.add(startDate.plusDays(i).format(formatter));
        }
        return dateList;
    }

    /**
     * list转map
     *
     * @param list
     * @return
     */
    private static Map<String, String> listToMap(List<CountDateNotificationRespDto> list) {
        return list.stream().collect(Collectors.toMap(countDateNotificationRespDto -> (Converter.toDateStr(countDateNotificationRespDto.getNotifyDate())), CountDateNotificationRespDto::getTotal));
    }

    /**
     * 获取当天的消息通知数
     *
     * @return
     */
    public Integer getNowDateTotal(String nowDate) {
        Integer nowDateTotalCache = cacheService.getNowDateTotalCache(nowDate);
        if (Objects.isNull(nowDateTotalCache)) {
            Integer total = queryNowDateTotal(nowDate);
            cacheService.setNowDateTotalCache(nowDate, total);
            return total;
        }
        return nowDateTotalCache;
    }

    /**
     * 查询数据库当天的消息通知数
     *
     * @param nowDateStr
     * @return
     */
    public Integer queryNowDateTotal(String nowDateStr) {
        Date nowDate = Converter.toDate(nowDateStr, DATE_FORMAT);
        Date endDate = DateUtil.dateAddDays(nowDate, 1);
        QueryWrapper<PublishMessagePO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().ge(PublishMessagePO::getNotifyTime, nowDate)
                .lt(PublishMessagePO::getNotifyTime, endDate)
                .eq(PublishMessagePO::getNotifyStatus, EnumPublishMessageNotifyStatus.DONE.name());
        Integer total = publishMessageMapper.countDateNotification(queryWrapper);
        return Optional.ofNullable(total).orElse(0);
    }

}
